
use crate::logger::Logger;

use std::time::Duration;
use std::thread::sleep;

struct Env ;

impl Env {
    pub fn new_logger(fname: &str) -> Option<Logger>{
        Some(Logger::new(fname))
    }

    // Returns the number of micro-seconds since some fixed point in time. 
    // Only useful for computing deltas of time.
    // uint64_t NowMicros()
    //NOTE: replace with std::time::Instant , see test::use_elapse_time;
    
    pub fn sleep_for_micros(micros: u64) {
        sleep(Duration::from_micros(micros));
    }

    // Start a new thread, invoking "function(arg)" within the new thread.
    // When "function(arg)" returns, the thread will be destroyed.
    // void StartThread(function, arg);
    //NOTE: replace with the rust thread::spawn(..)

    // TODO:
    //void Schedule(function, arg) 


    //filesystem DirEntry & FileEntry
    //DirEntry:  TODO:
    //  create_dir
    //  remove_dir
    //  rename_dir
    //  exist_dir()
    //  DirIterator;
    //File 
    //  create_file
    //  remove_file
    //  rename_file
    //  exist_file()
    //
    // File-Object
    //  new_AppendableFile
    //  new_WritableFile
    //  new_RandomAccessFile
    //  new_SequentialFile
}


#[cfg(test)]
mod test {

    mod logger {
        use crate::env::Env;
        use log::{trace, debug, info, warn, error};
        #[test]
        fn use_logger() {
            let expect_path = "./leveldb-rs.log";
            let logger = Env::new_logger(expect_path).unwrap();
            assert_eq!(expect_path, logger.path());

            trace!("hello world");
            debug!("hello world");
            info!("hello world");
            warn!("hello world");
            error!("hello world");
        }
    }//mod logger

    mod time {
        use crate::env::Env;
        use std::time::Instant;
        #[test]
        fn use_elapse_time() {
            let sw = Instant::now();
            Env::sleep_for_micros(5_000);
            assert!(sw.elapsed().as_micros() >= 5_000);
        }
    }//mod time


    mod thread {

        use std::thread;
        #[test]
        fn thread_no_args(){
            thread::spawn(move || {
                println!("thread_no_args");
            });
        }
        #[test]
        fn thread_no_args_with_join(){
            let handle = thread::spawn(move || {
                println!("thread_no_args_with_join");
            });
            let _ = handle.join();

        }

        #[test]
        fn thread_with_imm_copy(){
            let number = 3; // copy
            thread::spawn(move||{
                println!("thread_with_imm_copy :{}", number);
            });

        }
        #[test]
        fn thread_with_imm_borrow(){
            let s = String::from("imm string");
            thread::spawn(move||{
                let n = &s;
                println!("thread_with_imm_borrow :{}", n);
            });
            //Compile error : s is moved into thread closure
            // println!("thread_with_imm_borrow:{}", s);

        }

        #[test]
        fn thread_with_mut_copy(){
            let mut number = 0; //copy 
            thread::spawn( move || {
                for _ in 1..3 {
                    number += 1;
                    println!("thread_with_mut_copy : move mut number :{}", number);
                }
            });
            number += 1;
            println!("thread_with_mut_copy : local number:{}", number);
        }

        #[test]
        fn thread_with_mut_borrow(){
            let mut s = String::from("mut string");
            thread::spawn( move || {
                let ms = &mut s;
                ms.push_str("ok");
                println!("thread_with_mut_borrow : move mut string :{}", ms);
            });

            //Compile error : s is moved into thread closure
            // println!("thread_with_imm_borrow:{}", s);
            //s.push_str("lock ok");
        }

        #[test]
        fn thread_with_move(){
            let s = String::from("imm string");
            thread::spawn(move || {
                println!("thread_with_move: {}", s);
                drop(s);
            });
            //Compile error : s is moved into thread closure
            //println!("thread_with_move: {}", s);
        }

        mod channel{
            use std::sync::mpsc;
            use std::thread;
            #[test]
            fn channel_tx_rx(){
                let (tx, rx) = mpsc::channel();
                thread::spawn(move || {
                    tx.send(10).unwrap();
                });

                println!("from sub-thread -> recv: {}", rx.recv().unwrap());
            }
            #[test]
            fn channel_move_val_to_recv(){
                let (tx, rx) = mpsc::channel();
                thread::spawn(move || {
                    let s = String::from("sub thread string");
                    tx.send(s).unwrap();
                    //println!("after send: can we use the string:{} ?", s);
                });

                println!("channel_move_val_to_recv: from sub-thread -> {}", rx.recv().unwrap());
            }

            #[test]
            fn channel_many_tx_one_rx(){
                let (tx, rx) = mpsc::channel();

                let tx1 = tx.clone();
                thread::spawn( move || {
                    let s = String::from("sub-thread-1 string");
                    tx1.send(s).unwrap();
                });

                let tx2 = tx.clone();
                thread::spawn( move || {
                    let s = String::from("sub-thread-2 string");
                    tx2.send(s).unwrap();
                });

                thread::spawn( move || {
                    let s = String::from("sub-thread-3 string");
                    tx.send(s).unwrap();
                });


                for val in rx {
                    println!("channel_many_tx_one_rx: recv message: {}", val);
                }

            }
            use std::time::Duration;
            #[test]
            fn channel_current_thread_sleep_for_a_while(){
                let (tx, rx) = mpsc::channel();

                thread::spawn( move || {
                    let s = String::from("sub-thread-1 string");
                    tx.send(s).unwrap();
                });
                
                thread::sleep(Duration::from_micros(60_000));
                println!("channel_current_thread_sleep_for_a_while: recv: {}", rx.recv().unwrap());

            }
        }//mod channel


        mod mutex {
            use std::sync::Mutex;
            #[test]
            fn mutex_self(){
                let m = Mutex::new(5);
                {
                    let mut scope_ref_m = m.lock().unwrap();
                    *scope_ref_m = 6; 
                }
                println!("mutex_self: m = {:?}", m);
            }


            use std::thread;
            use std::sync::Arc;
            #[test]
            fn mutex_multi_threads(){

                let counter = Arc::new(Mutex::new(0));
                let mut handles = vec![];
                for _ in 0 .. 10 {
                    let counter = Arc::clone(&counter);
                    let handle = thread::spawn(move || {
                        let mut num = counter.lock().unwrap();
                        *num += 1;
                    });
                    handles.push(handle);
                }

                for handle  in handles {
                    handle.join().unwrap();
                }
                println!("mutex_multi_threads: Result {}", *counter.lock().unwrap());
            }
        }//mod mutex

    }//mod thread

    mod dir {
        use std::path::Path;
        use std::fs::DirBuilder;
        fn assert_dir_not_exist(path: &str) {
            if Path::new(path).exists() {
                std::fs::remove_dir_all(path).unwrap();
            }
        }
        #[test]
        fn remove(){
            let path = "./dir_remove__abc.rs";
            assert_dir_not_exist(path);
        }
        #[test]
        fn empty_dir_create(){
            let path = "./dir_create__abc.rs";
            assert_dir_not_exist(path);
            assert!(!Path::new(path).exists()); 

            //new empty dir
            DirBuilder::new().create(path).unwrap(); 
            //test is_dir && dir exist ok
            assert!(std::fs::metadata(path).unwrap().is_dir());
            assert!(Path::new(path).exists());

            //remove empty dir
            assert_dir_not_exist(path);
        }

        #[test]
        fn no_empty_dir_creation(){
            let path = "./no_empty_dir_a/b/c/";
            assert_dir_not_exist(path);
            assert!(!Path::new(path).exists()); //try_exists??// path.try_exists()?

            DirBuilder::new().recursive(true).create(path).unwrap();
            assert!(std::fs::metadata(path).unwrap().is_dir());
            assert!(Path::new(path).exists());
            
            let no_empty_dir = "./no_empty_dir_a/";
            std::fs::remove_dir_all(no_empty_dir).unwrap(); //remove all dir
            assert!(!Path::new(path).exists());

            assert_dir_not_exist(path);
        }

        #[test]
        fn rename(){
            let path = "./dir_rename__abc.rs";
            assert_dir_not_exist(path);
            let new_path = "./dir_rename__bcd.rs";
            assert_dir_not_exist(new_path);

            //create path 
            DirBuilder::new().recursive(true).create(path).unwrap();
            //path create ok && new_path not ok
            assert!(Path::new(path).exists());
            assert!(!Path::new(new_path).exists());

            std::fs::rename(path, new_path).unwrap();
            
            //path not-exists; new_path exists
            assert!(!Path::new(path).exists());
            assert!(Path::new(new_path).exists());

            //clear the inter dirs
            assert_dir_not_exist(path);
            assert_dir_not_exist(new_path);
        }
    }//mod dir

    mod file {
        use std::path::Path;
        fn assert_file_not_exist(fpath: &str) {
            if Path::new(fpath).exists() {
                std::fs::remove_file(fpath).unwrap();
            }
        }
        #[test]
        fn exist(){
            let fpath = "./Cargo.toml";
            assert!(Path::new(fpath).exists());
        }
        #[test]
        fn not_exist(){
            let fpath = "./file_not_exist.toml";
            assert!(!Path::new(fpath).exists());
        }

        use std::fs::File;
        #[test]
        fn create(){
            let fpath = "./create_file.toml";
            assert_file_not_exist(fpath);

            File::create(fpath).unwrap();
            assert!(Path::new(fpath).exists());

            assert_file_not_exist(fpath);
        }
        #[test]
        fn remove(){
            let fpath = "./remove_file..toml";
            assert_file_not_exist(fpath);
        }
        #[test]
        fn rename(){
            let fpath = "./rename_old_file.toml";
            assert_file_not_exist(fpath);
            let new_fpath = "./rename_new_file.toml";
            assert_file_not_exist(new_fpath);

            File::create(fpath).unwrap();
            assert!(Path::new(fpath).exists());
            assert!(!Path::new(new_fpath).exists());

            std::fs::rename(fpath, new_fpath).unwrap();

            assert!(!Path::new(fpath).exists());
            assert!(Path::new(new_fpath).exists());

            assert_file_not_exist(fpath);
            assert_file_not_exist(new_fpath);
        }
    }

    mod file_object {
        //TODO: 
        // convenience way to read_to_string | write ...
        // create file object:  create , OpenOptions.open 
        // Read, Write, 
        // Flush, Sync
        // Seek
    }

}//mod test

/*
 * to be noted && next study:  
 *   std::time=>   
 *      Instant => StopWatch
 *      Duration => from & as 
 *      SystemTime => Wall Time
 *
 *   std::thread => thread-related
 *
 *   std::sync =>  
 *      Primitives
 *      mpsc::channel
 *
 *   keyword - async && std::future && std::task
 *
 */
